/* 日志落地模块 */
#ifndef __M_SINK_H_
#define __M_SINK_H_

#include "util.hpp"
#include <memory>
#include <fstream>
#include <cassert>
#include <sstream>

namespace log{
    class logSink{
        public:
            using ptr = std::shared_ptr<logSink>;
            logSink(){}
            virtual ~logSink(){} 
            virtual void Log(const char* data, size_t len) = 0;
    };
    //落地方向：标准输出
    class StdoutSink : public logSink{
        public: 
            //将日志消息写入到标准输出
            void Log(const char* data, size_t len){
                std::cout.write(data, len);
            }
    };
    //落地方向：指定文件
    class FileSink : public logSink{
        public: 
            //将日志消息写入到指定文件
            FileSink(const std::string& pathname):_pathname(pathname){
                //1.创建日志文件所存放的目录
                util::File::createDirectory(util::File::path(pathname));
                //2.创建并打开日志文件
                _ofs.open(_pathname, std::ios::binary | std::ios::app); //以追加模式写入到文件中
            }
            void Log(const char* data, size_t len){
                _ofs.write(data, len);
                //判断写入是否出错
                assert(_ofs.good());
            }
        private:
            std::string _pathname;
            std::ofstream _ofs;
    };
    //落地方向：滚动文件
    class RollBySizeSink : public logSink{
        public:
            //构造时传入文件名，并打开文件，将操作句柄管理起来
            RollBySizeSink(const std::string& _basename, size_t max_size)
            :_basename(_basename), _max_fsize(max_size), _cur_fsize(0), _name_count(0)
            {
                std::string pathname = createNewFile();
                //1.创建日志文件所存放的目录
                util::File::createDirectory(util::File::path(pathname));
                //2.创建并打开日志文件
                _ofs.open(pathname, std::ios::binary | std::ios::app);
                assert(_ofs.is_open()); 
            }
            //写入前判断文件大小，如果超过了最大大小则需要切换文件
            void Log(const char* data, size_t len){
                if(_cur_fsize >= _max_fsize){
                    _ofs.close(); //关闭原来打开的日志文件
                    std::string pathname = createNewFile();
                    _ofs.open(pathname, std::ios::binary | std::ios::app); //以追加模式写入到文件中
                    assert(_ofs.is_open());
                    _cur_fsize = 0;
                }

                _ofs.write(data, len);
                assert(_ofs.good());
                _cur_fsize += len;
            }
        private:
            //进行大小判断，超过文件指定大小则创建新文件
            std::string createNewFile() {
                time_t t = util::Date::now();
                struct tm lt;
                localtime_r(&t, &lt);
                std::stringstream filename;
                filename << _basename;
                filename << lt.tm_year + 1900;
                filename << lt.tm_mon + 1;
                filename << lt.tm_mday;
                filename << lt.tm_hour;
                filename << lt.tm_min;
                filename << lt.tm_sec;
                filename << "-";
                filename << _name_count++;
                filename << ".log";
                return filename.str();
            }
        private:
            size_t _name_count;
            std::string _basename; // ./logs/base -》 ./logs/base-20230809122431.log
            std::ofstream _ofs;
            size_t _max_fsize; //记录文件最大大小
            size_t _cur_fsize; //记录文件当前大小
    };
    //简单工厂模式
    class SinkFactory{
        public:
            template <typename SinkType, typename ...Args>
            static logSink::ptr create(Args&& ...args){
                return std::make_shared<SinkType>(std::forward<Args>(args)...);
            }
    };
}

#endif